home *** CD-ROM | disk | FTP | other *** search
/ Power DOS 1996 July / Power DOS - July 1996.iso / sound / c_labs / devinfo / recplay.exe / DELAY.C next >
Encoding:
C/C++ Source or Header  |  1996-02-09  |  23.6 KB  |  800 lines

  1. /* -------------------------------------------------------------------------- */
  2. /*                                                                            */
  3. /* (C) Copyright Creative Technology Ltd 1994-1996. All right reserved        */
  4. /*                                                                            */
  5. /* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY      */
  6. /* KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE        */
  7. /* IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR      */
  8. /* PURPOSE.                                                                   */
  9. /*                                                                            */
  10. /* You have a royalty-free right to use, modify, reproduce and                */
  11. /* distribute the Sample Files (and/or any modified version) in               */
  12. /* any way you find useful, provided that you agree that                      */
  13. /* Creative has no warranty obligations or liability for any Sample Files.    */
  14. /*                                                                            */
  15. /*----------------------------------------------------------------------------*/
  16.  
  17. /* DELAY.C
  18.  *
  19.  * D. Kaden 6/22/94
  20.  *
  21.  * Copyright (c) 1993, 1994 Creative Labs, Inc.
  22.  *
  23.  * This program records and plays simultaneously using two Sound Blaster
  24.  * cards.
  25.  *
  26.  * In addition to the normal BLASTER environment variable, you should also
  27.  * have a BLASTER2 environment variable for the second Sound Blaster card.
  28.  *
  29.  * For example, your environment should look something like this:
  30.  *
  31.  *    BLASTER=A220 I5 D1 H5 P330 T6
  32.  *    BLASTER2=A240 I7 D3 H6 P330 T6
  33.  *
  34.  * The first card (BLASTER) does the recording.  The second (BLASTER2) does
  35.  * the playback.
  36.  *
  37.  */
  38.  
  39. #include <stdio.h>
  40. #include <stdlib.h>
  41. #include <ctype.h>
  42. #include <conio.h>
  43. #include <dos.h>
  44. #include <mem.h>
  45. #include <fcntl.h>
  46. #include "mydefs.h"
  47. #include "sbdefs.h"
  48.  
  49. #define FILENAME "delay.raw"
  50.  
  51.  
  52. // Structure for storing data associated with each SB card.
  53. typedef struct {
  54.    int base,                         // SB card base I/O address
  55.     MixerAddr, MixerData,            // mixer chip addresses
  56.     Reset, WrBuf,                    // DSP addresses
  57.     DataAvail, ReadData,             // DSP addresses
  58.     irq,                             // IRQ number
  59.     dma8,                            // 8-bit DMA channel
  60.     dma16,                           // 16-bit DMA channel
  61.     midi,                            // midi port
  62.     cardtype;                        // card type number from environment
  63.     void interrupt (*intvecsave)();  // previous interrupt vector
  64.     int intrnum;                     // interrupt number
  65.     int intrmask;                    // interrupt mask
  66.    } cardinfo;
  67.  
  68.  
  69. // Structure for retrieving DMA controller register port addresses.
  70. typedef struct {
  71.    unsigned char addr, count, page, mask, mode, FF;
  72.    } dmaportstruct;
  73.  
  74.  
  75. // Constant array that defines port addresses for DMA controller registers.
  76. const dmaportstruct dmaportarray[8] = {
  77.     {0x0,  0x1,  0x87, 0xA,  0xB,  0xC},   // chan 0
  78.     {0x2,  0x3,  0x83, 0xA,  0xB,  0xC},   // chan 1
  79.     {0x4,  0x5,  0x81, 0xA,  0xB,  0xC},   // chan 2  DON'T USE
  80.     {0x6,  0x7,  0x82, 0xA,  0xB,  0xC},   // chan 3
  81.     {0x0,  0x0,  0x00, 0xD4, 0xD6, 0xD8},  // chan 4  DON'T USE
  82.     {0xC4, 0xC6, 0x8B, 0xD4, 0xD6, 0xD8},  // chan 5
  83.     {0xC8, 0xCA, 0x89, 0xD4, 0xD6, 0xD8},  // chan 6
  84.     {0xCC, 0xCE, 0x8A, 0xD4, 0xD6, 0xD8}   // chan 7
  85.    };
  86.  
  87.  
  88.  
  89.  
  90. #define BUFSIZE (8*1024)      // size of the DMA buffer
  91. #define NUMBUFS 2
  92. #define TIMECONST 165         // 165 = approx 11.5 KHz sample rate
  93.  
  94.  
  95. cardinfo      card1, card2;
  96.  
  97.               // It would be better to dynamically allocate the DMA buffers
  98.               // to guarantee that they don't cross a DMA page boundary.
  99.               // This is quick and dirty, but it usually works.
  100. unsigned char recbuf[BUFSIZE],         // recording DMA buffer
  101.               playbuf[BUFSIZE];        // playback DMA buffer
  102.  
  103. unsigned int  inpage,   outpage,       // DMA pages for input and output buffers
  104.               inoffset, outoffset,     // offsets for DMA input and output buffers
  105.               count = BUFSIZE;         // size of the DMA buffers
  106.  
  107. char          *inbuffers[NUMBUFS];     // Input (recording) buffers to
  108.                                        //  transfer data from DMA buffer
  109.                                        //  to disk.
  110. char          *outbuffers[NUMBUFS];    // Output (playback) buffers to
  111.                                        //  transfer data from disk to DMA
  112.                                        //  buffer.
  113.  
  114. int           NumFilledInBufs   = 0,   // number of filled input buffers
  115.               NumFilledOutBufs  = 0,   // number of filled output buffers
  116.               NumRecBufToFill   = 0,   // next input buffer to fill from DMA buffer
  117.               NumRecBufToEmpty  = 0,   // next input buffer to copy to disk
  118.               NumPlayBufToFill  = 0,   // next output buffer to fill from disk
  119.               NumPlayBufToEmpty = 0,   // next output buffer to copy to DMA buffer
  120.               HalfBufToEmpty    = 0,   // which half of input DMA buffer to empty
  121.               HalfBufToFill     = 0,   // which half of output DMA buffer to fill
  122.               NumBufsWritten    = 0;   // number of buffers written to disk
  123.  
  124.  
  125.  
  126. int base16(char **str, int *val)
  127. /* Takes a double pointer to a string, interprets the characters as a
  128.  * base-16 number, and advances the pointer.
  129.  * Returns 0 if successful, 1 if not.
  130.  */
  131. {
  132.    char c;
  133.    int digit;
  134.    *val = 0;
  135.  
  136.    while ((**str != ' ') && (**str != '\0')) {
  137.       c = toupper(**str);
  138.       if (c >= '0' && c <= '9')
  139.          digit = c - '0';
  140.       else if (c >= 'A' && c <= 'F')
  141.          digit = c - 'A'  + 10;
  142.       else
  143.          return 1;          // error in string
  144.  
  145.       *val = *val * 16 + digit;
  146.       (*str)++;
  147.    }
  148.    return 0;
  149. }
  150.  
  151.  
  152.  
  153. int base10(char **str, int *val)
  154. /* Takes a double pointer to a string, interprets the characters as a
  155.  * base-10 number, and advances the pointer.
  156.  * Returns 0 if successful, 1 if not.
  157.  */
  158. {
  159.    char c;
  160.    int digit;
  161.    *val = 0;
  162.  
  163.    while ((**str != ' ') && (**str != '\0')) {
  164.       c = toupper(**str);
  165.       if (c >= '0' && c <= '9')
  166.          digit = c - '0';
  167.       else
  168.          return 1;          // error in string
  169.  
  170.       *val = *val * 10 + digit;
  171.       (*str)++;
  172.    }
  173.    return 0;
  174. }
  175.  
  176.  
  177.  
  178. int ReadBlasterEnv(char *name, int *port, int *irq, int *dma8, int *dma16,
  179.  int *midi, int *cardtype)
  180. /* Gets the Blaster environment statement and stores the values in the
  181.  * variables whose addresses were passed to it.
  182.  *
  183.  * Input:
  184.  *   name  - environment name to get, usually "BLASTER"
  185.  *
  186.  * Returns:
  187.  *   0  if successful
  188.  *   1  if there was an error reading the port address.
  189.  *   2  if there was an error reading the IRQ number.
  190.  *   3  if there was an error reading the 8-bit DMA channel.
  191.  *   4  if there was an error reading the 16-bit DMA channel.
  192.  *   5  if there was an error reading the MIDI address.
  193.  *   6  if there was an error reading the card type number.
  194.  */
  195. {
  196.    char     *env;
  197.    unsigned val;
  198.    int      digit;
  199.  
  200.    env = getenv(name);
  201.  
  202.    if (!env)
  203.       return 7;   // if there was no environment entry for name.
  204.  
  205.    while (*env) {
  206.       switch(toupper( *(env++) )) {
  207.          case 'A':
  208.             if (base16(&env, port))
  209.                return 1;
  210.             break;
  211.          case 'I':
  212.             if (base10(&env, irq))
  213.                return 2;
  214.             break;
  215.          case 'D':
  216.             if (base10(&env, dma8))
  217.                return 3;
  218.             break;
  219.          case 'H':
  220.             if (base10(&env, dma16))
  221.                return 4;
  222.             break;
  223.          case 'P':
  224.             if (base16(&env, midi))
  225.                return 5;
  226.             break;
  227.          case 'T':
  228.             if (base10(&env, cardtype))
  229.                return 6;
  230.             break;
  231.          default:
  232.             break;
  233.       }
  234.    }
  235.  
  236.    return 0;
  237. }
  238.  
  239.  
  240.  
  241. void SegToPhys(unsigned char far *ptr, unsigned long size, unsigned long *physaddr, unsigned long *endaddr)
  242. // Converts a segmented address to a physical memory address.
  243. {
  244.    *physaddr=(unsigned long)(((unsigned long)FP_SEG(ptr) << 4) + FP_OFF(ptr));
  245.    *endaddr = *physaddr+size-1;
  246. }
  247.  
  248.  
  249.  
  250. unsigned int PhysToPage(unsigned long physaddr, unsigned long endaddr, unsigned int *page, unsigned int *offset)
  251. /* Converts a physical memory address to a DMA-page address.  Returns the
  252.  * number of bytes in this block of memory that aren't in a different DMA
  253.  * page.
  254.  *  INPUT:
  255.  *   physaddr - a physical (not segmented) address specifying the start
  256.  *              of the DMA buffer.
  257.  *   endaddr  - a physical address specifying the last byte of the DMA
  258.  *              buffer.
  259.  *  OUTPUT:
  260.  *   page     - the DMA page of the buffer.
  261.  *   offset   - the offset of the buffer.
  262.  *  RETURN VALUE:
  263.  *   The minimum of (endaddr-physaddr+1, number of bytes in this page
  264.  *   starting at physaddr).
  265.  */
  266. {
  267.    unsigned int remaining;
  268.    *page=physaddr >> 16;
  269.    *offset=physaddr & 0xFFFF;
  270.  
  271.    remaining=0xFFFF - *offset + 1;   /* bytes remaining in page */
  272.    if (remaining>endaddr-physaddr)   /* if more bytes left in page than in buffer */
  273.       return endaddr-physaddr+1;     /* return number of bytes in buffer */
  274.    else return remaining;            /* else return number of bytes left in page */
  275. }
  276.  
  277.  
  278.  
  279. void dspout(cardinfo *card, unsigned int val)
  280. // Output a byte to the Sound Blaster Digital Sound Processor.
  281. {
  282.    while (inp(card->WrBuf) & 0x80)
  283.       ;
  284.    outp(card->WrBuf, val);
  285. }
  286.  
  287.  
  288.  
  289. boolean dspin(cardinfo *card, unsigned int *val)
  290. // Read a byte from the Digital Sound Processor.
  291. {
  292.    while (!(inp(card->DataAvail) & 0x80)) {
  293.       ;
  294.    }
  295.    *val=inp(card->ReadData);
  296.  
  297.    return true;    // used to be meaningful
  298. }
  299.  
  300.  
  301.  
  302. void SetupDMA(int dmachan, unsigned short page, unsigned short ofs,
  303.  unsigned short DMAcount, unsigned char dmacmd)
  304. // This programs the DMA controller.
  305. // NOTE:  This function supports 8-bit DMA channels ONLY (because this pro-
  306. // gram only uses 8-bit voice mode.  The SetupDMA function in the program
  307. // DELAY2.C supports both 8- and 16-bit DMA channels.
  308. {
  309.    dmaportstruct dmaports;
  310.  
  311. /**** There MUST be parens around the values to be output, or the compiler
  312.  **** will convert them to unsigned chars BEFORE it does the operations
  313.  **** on them.
  314.  ****/
  315.    dmaports = dmaportarray[dmachan];
  316.  
  317.    outp(dmaports.mask, 4 | dmachan);            // mask off dma channel
  318.    outp(dmaports.FF, 0);                        // clear flip-flop
  319. /* This next value to DMAC is different between auto-init and non-auto-init.
  320.  * (0x58 vs. 0x48) */
  321.    outp(dmaports.mode , dmacmd | dmachan);      // set mode
  322.    outp(dmaports.addr, (ofs & 0xFF));           // low byte base addr
  323.    outp(dmaports.addr, (ofs >> 8));             // high byte base addr
  324.    outp(dmaports.page, page);                   // physical page number
  325.    outp(dmaports.count,((DMAcount-1) & 0xFF));  // count low byte
  326.    outp(dmaports.count,((DMAcount-1) >> 8));    // count high byte
  327.    outp(dmaports.mask, dmachan);                // enable dma channel
  328. }
  329.  
  330.  
  331.  
  332. void SetupDSP(cardinfo *card, unsigned char dspcmd, unsigned short DSPcount,
  333.  unsigned char tc)
  334. // Programs the Sound Blaster card.
  335. {
  336.    // Program the time constant.
  337.    dspout(card, dspcmdTimeConst);
  338.    dspout(card, tc);
  339.  
  340.    // Program the DMA buffer size.
  341.    dspout(card, dspcmdBlockSize);
  342.    dspout(card, (DSPcount-1) & 0xFF);
  343.    dspout(card, (DSPcount-1) >> 8);
  344.  
  345.    // Send the play or record command.
  346.    dspout(card, dspcmd);
  347. }
  348.  
  349.  
  350.  
  351. void SetupInputDMA(unsigned int page, unsigned int ofs, int count,
  352.  unsigned char tc)
  353. // Program the DMA Controller and Sound Blaster for DMA input.
  354. {
  355.    SetupDMA(card1.dma8, page, ofs, count, DMAMODEWRITE);
  356.    SetupDSP(&card1, dspcmdAUTODMAADC, count/2, tc);
  357. }
  358.  
  359.  
  360.  
  361. void SetupOutputDMA(unsigned int page, unsigned int ofs, int count,
  362.  unsigned char tc)
  363. // Program the DMA Controller and Sound Blaster for DMA output.
  364. {
  365.    SetupDMA(card2.dma8, page, ofs, count, DMAMODEREAD);
  366.    SetupDSP(&card2, dspcmdAUTODMADAC, count/2, tc);
  367. }
  368.  
  369.  
  370.  
  371. void SetMixer(cardinfo *card1, cardinfo *card2)
  372. // Resets the SB cards and selects the proper input.
  373. {
  374.  
  375.    unsigned int val;
  376.  
  377.    /* reset sb card #1 */
  378.    outp(card1->Reset,1);
  379.    delay(1);
  380.    outp(card1->Reset,0);
  381.    if (dspin(card1, &val))
  382.       if (val!=dspReady)
  383.          printf("Sound Blaster card #1 not ready.");
  384.  
  385.    switch (card1->cardtype) {
  386.       case 2:
  387.       case 4:     // SBPRO
  388.          outp(card1->MixerAddr, ADCSELECT);
  389.          outp(card1->MixerData, 0);             // select microphone
  390.          dspout(card1, 0xD3);                   // turn speaker off
  391.          break;
  392.       case 6:     // SB16
  393.          outp(card1->MixerAddr, SB16INPUTL);
  394.          outp(card1->MixerData, 1);             // select microphone
  395.          outp(card1->MixerAddr, SB16INPUTR);
  396.          outp(card1->MixerData, 1);             // select microphone
  397.          break;
  398.    }
  399.  
  400.    /* reset sb card #2 */
  401.    outp(card2->Reset,1);
  402.    delay(1);
  403.    outp(card2->Reset,0);
  404.    if (dspin(card2,&val))
  405.       if (val!=dspReady)
  406.          printf("Sound Blaster card #2 not ready.");
  407.  
  408.    switch (card2->cardtype) {
  409.       case 2:
  410.       case 4:     // SBPRO
  411.          dspout(card2,0xD1);        // turn speaker on
  412.          break;
  413.       case 6:
  414.          outp(card2->MixerAddr, SB16INPUTL);
  415.          outp(card2->MixerData, 0);             // select nothing
  416.          outp(card2->MixerAddr, SB16INPUTR);
  417.          outp(card2->MixerData, 0);             // select nothing
  418.          break;
  419.    }
  420. }
  421.  
  422.  
  423.  
  424. void InitScreen(void)
  425. {
  426.    clrscr();
  427.    printf("Two Sound Blaster Delay program.\nPress any key to stop.\n");
  428. }
  429.  
  430.  
  431.  
  432. void DrainInputBuffer(void)
  433. // Copies half the DMA buffer to one of the record buffers.
  434. {
  435.    // Copy data from current half of DMA buffer to next input buffer. 
  436.    memcpy(inbuffers[NumRecBufToFill], recbuf + HalfBufToEmpty*BUFSIZE/2, BUFSIZE/2);
  437.  
  438.    // Make next buffer current
  439.    ++NumRecBufToFill;
  440.    if (NumRecBufToFill >= NUMBUFS)
  441.       NumRecBufToFill =  0;
  442.  
  443.    // Increment count of filled input buffers.
  444.    ++NumFilledInBufs;
  445.  
  446.    // Next time use other half of DMA buffer.
  447.    HalfBufToEmpty ^= 1;
  448.  
  449. }
  450.  
  451.  
  452. void FillOutputBuffer(void)
  453. // Copies one of the output buffers to half the DMA buffer.
  454. {
  455.    // Copy data from next output buffer to current half of DMA buffer.
  456.    memcpy(playbuf + HalfBufToFill*BUFSIZE/2, outbuffers[NumPlayBufToEmpty], BUFSIZE/2);
  457.  
  458.    // Make next buffer current
  459.    ++NumPlayBufToEmpty;
  460.    if (NumPlayBufToEmpty >= NUMBUFS)
  461.       NumPlayBufToEmpty =  0;
  462.  
  463.    // Decrement count of filled output buffers.
  464.    --NumFilledOutBufs;
  465.  
  466.    // Next time use other half of DMA buffer.
  467.    HalfBufToFill ^= 1;
  468. }
  469.  
  470.  
  471.  
  472. void interrupt InputSBISR(void)
  473. // Service interrupts from SB doing input.
  474. {
  475.    // Make sure this wasn't a spurious interrupt.
  476.    if (card1.irq == 7) {
  477.       outp(PIC1MODE, 0xB);                // select In Service Register
  478.       if ((inp(PIC1MODE) & 0x80) == 0)    // if bit 7 == 0, spurious interrupt
  479.          return;
  480.    }
  481.  
  482.    inp(card1.DataAvail);                  // acknowledge interrupt
  483.  
  484.    DrainInputBuffer();
  485.  
  486.    if (card1.irq > 8)                     // If irq 10, send EOI to second PIC.
  487.       outp(PIC2MODE, PICEOI);
  488.    outp(PIC1MODE,PICEOI);                 // Send EOI to first PIC.
  489. }
  490.  
  491.  
  492.  
  493. void interrupt OutputSBISR(void)
  494. // Service interrupts from SB doing output.
  495. {
  496.    // Make sure this wasn't a spurious interrupt.
  497.    if (card2.irq == 7) {
  498.       outp(PIC1MODE, 0xB);                // select In Service Register
  499.       if ((inp(PIC1MODE) & 0x80) == 0)    // if bit 7 == 0, spurious interrupt
  500.          return;
  501.    }
  502.  
  503.    inp(card2.DataAvail);                  // acknowledge interrupt
  504.  
  505.    FillOutputBuffer();
  506.  
  507.    if (card2.irq > 8)                     // If irq 10, send EOI to second PIC.
  508.       outp(PIC2MODE, PICEOI);
  509.    outp(PIC1MODE,PICEOI);                 // Send EOI to first PIC.
  510. }
  511.  
  512.  
  513.  
  514.  
  515.  
  516. int GetCardInfo(char *name, cardinfo *card)
  517. // Read a blaster environment string.
  518. {
  519.    int result;
  520.    int dummy;
  521.  
  522.    result=ReadBlasterEnv(name, &card->base, &card->irq, &card->dma8, &dummy,
  523.     &dummy, &card->cardtype);
  524.  
  525.    if (result != 0) {
  526.       switch (result) {
  527.          case 1:
  528.             printf("Error in %s port address.\n",name);
  529.             break;
  530.          case 2:
  531.             printf("Error in %s IRQ number.\n",name);
  532.             break;
  533.          case 3:
  534.             printf("Error in %s 8-bit DMA channel.\n",name);
  535.             break;
  536.          case 4:
  537.             printf("Error in %s 16-bit DMA channel.\n",name);
  538.             break;
  539.          case 5:
  540.             printf("Error in %s MIDI address.\n",name);
  541.             break;
  542.          case 6:
  543.             printf("Error in %s card type number.\n",name);
  544.             break;
  545.          case 7:
  546.             printf("Error:  %s environment variable not set.\n",name);
  547.             break;
  548.       }
  549.    }
  550.  
  551.    card->WrBuf     = card->base + dspoffsetWrBuf;
  552.    card->Reset     = card->base + dspoffsetReset;
  553.    card->ReadData  = card->base + dspoffsetReadData;
  554.    card->DataAvail = card->base + dspoffsetDataAvail;
  555.    card->MixerAddr = card->base + dspoffsetMixerAddr;
  556.    card->MixerData = card->base + dspoffsetMixerData;
  557.  
  558.    return result;
  559. }
  560.  
  561.  
  562.  
  563. void EnableCardInterrupt(cardinfo *card, void interrupt (*newvect)())
  564. {
  565.    int intrmask;
  566.  
  567.    // calculate interrupt number for IRQ
  568.    if (card->irq < 8)
  569.       card->intrnum = card->irq + 8;          // IRQs 0-7 map to interrupts 8-15.
  570.    else
  571.       card->intrnum = card->irq - 8 + 0x70;   // IRQs 8-15 map to interrupts 70H-78H.
  572.  
  573.    // generate 16-bit mask for IRQ
  574.    card->intrmask = 1 << card->irq;
  575.  
  576.    card->intvecsave = getvect(card->intrnum); // save previous interrupt vector
  577.  
  578.    setvect(card->intrnum, newvect);           // set new interrupt vector
  579.  
  580.    // enable interrupts at interrupt controllers
  581.    intrmask = card->intrmask;
  582.    outp(PIC1MASK, inp(PIC1MASK) & ~intrmask);
  583.    intrmask >>= 8;
  584.    outp(PIC2MASK, inp(PIC2MASK) & ~intrmask);
  585. }
  586.  
  587.  
  588.  
  589. void DisableCardInterrupt(cardinfo *card)
  590. {
  591.    int intrmask;
  592.  
  593.    // disable interrupts at interrupt controllers
  594.    intrmask = card->intrmask;
  595.    outp(PIC1MASK, inp(PIC1MASK) | intrmask);
  596.    intrmask >>= 8;
  597.    outp(PIC2MASK, inp(PIC2MASK) | intrmask);
  598.  
  599.    // Restore previous vector
  600.    setvect(card->intrnum, card->intvecsave);
  601. }
  602.  
  603.  
  604.  
  605. int CompareCards(cardinfo *card1, cardinfo *card2)
  606. // Make sure the two SB cards don't have the same settings.
  607. {
  608.    int result = 0;
  609.  
  610.    if (card1->base == card2->base) {
  611.       printf("Error:  Cards are at the same I/O address: %X\n",card1->base);
  612.       result |= 1;
  613.    }
  614.  
  615.    if (card1->irq == card2->irq) {
  616.       printf("Error:  Cards are using the same interrupt: %d\n",card1->irq);
  617.       result |= 2;
  618.    }
  619.  
  620.    if (card1->dma8 == card2->dma8) {
  621.       printf("Error:  Cards are using the same DMA channel: %d\n",card1->dma8);
  622.       result |= 4;
  623.    }
  624.  
  625.    return result;
  626. }
  627.  
  628.  
  629. void WriteBufsToDisk(int handle)
  630. // Writes all the record buffers that have data to the disk.
  631. {
  632.    unsigned nread;
  633.  
  634.    // While there are input buffers with data, write them to disk
  635.    while (NumFilledInBufs > 0) {
  636.       _dos_write(handle, inbuffers[NumRecBufToEmpty], BUFSIZE/2, &nread);
  637.       ++NumBufsWritten;
  638.  
  639.       --NumFilledInBufs;
  640.  
  641.       ++NumRecBufToEmpty;
  642.       if (NumRecBufToEmpty >= NUMBUFS)
  643.          NumRecBufToEmpty = 0;
  644.    }
  645. }
  646.  
  647.  
  648.  
  649. void ReadBufsFromDisk(int handle)
  650. // Fills all empty playback buffers from disk.
  651. {
  652.    unsigned nread;
  653.  
  654.    // While some output buffers are empty, fill them from disk
  655.    while (NumFilledOutBufs < NUMBUFS) {
  656.       _dos_read(handle, outbuffers[NumPlayBufToFill], BUFSIZE/2, &nread);
  657.  
  658.       ++NumFilledOutBufs;
  659.  
  660.       ++NumPlayBufToFill;
  661.       if (NumPlayBufToFill >= NUMBUFS)
  662.          NumPlayBufToFill = 0;
  663.    }
  664. }
  665.  
  666.  
  667.  
  668.  
  669.  
  670. void main(void)
  671. {
  672.    int i,b,c;
  673.    unsigned long physaddr,endaddr;
  674.    boolean done=false;
  675.    int result;
  676.    int rech, playh;       // file handles for recording and playing
  677.    char fname[] = FILENAME;
  678.  
  679.    result=GetCardInfo("BLASTER", &card1);
  680.    if (result != 0)
  681.       return;
  682.  
  683.    result=GetCardInfo("BLASTER2", &card2);
  684.    if (result != 0)
  685.       return;
  686.  
  687.    if (CompareCards(&card1, &card2))
  688.       return;
  689.  
  690.    SegToPhys(recbuf,BUFSIZE,&physaddr,&endaddr);
  691.    if (PhysToPage(physaddr,endaddr,&inpage,&inoffset) < BUFSIZE) {
  692.       printf("The DMA buffer crossed a page boundary.  Sorry, I didn't write the program\n");
  693.       printf("to deal with this.  See comments in program.  Terminating.\n");
  694.       return;
  695.       /*
  696.        * The program should dynamically allocate buffers until a buffer is
  697.        * found that doesn't cross a DMA page boundary.  I took a short cut
  698.        * here by just using an array, which usually works if it's much less
  699.        * than 64K bytes.
  700.        */
  701.    }
  702.  
  703.    SegToPhys(playbuf,BUFSIZE,&physaddr,&endaddr);
  704.    if (PhysToPage(physaddr,endaddr,&outpage,&outoffset) < BUFSIZE) {
  705.       printf("The DMA buffer crossed a page boundary.  Sorry, I didn't write the program\n");
  706.       printf("to deal with this.  See comments in program.  Terminating.\n");
  707.       return;
  708.    }
  709.  
  710.  
  711.    InitScreen();
  712.  
  713.  
  714.    // allocate buffers to transfer data between DMA buffer and disk
  715.    for (i=0; i<NUMBUFS; i++) {
  716.       inbuffers[i] = malloc(BUFSIZE/2);
  717.       if (!inbuffers[i]) {
  718.          printf("Error:  memory couldn't be allocated.\n");
  719.          return;
  720.       }
  721.       outbuffers[i] = malloc(BUFSIZE/2);
  722.       if (!outbuffers[i]) {
  723.          printf("Error:  memory couldn't be allocated.\n");
  724.          return;
  725.       }
  726.    }
  727.  
  728.    if (_dos_creat(fname, 0, &rech)) {
  729.       printf("Couldn't open file %s for writing.\n",fname);
  730.       return;
  731.    }
  732.  
  733.    if (_dos_open(fname, O_RDONLY, &playh)) {
  734.       printf("Couldn't open file %s for reading.\n",fname);
  735.       return;
  736.    }
  737.  
  738.    SetMixer(&card1, &card2);
  739.  
  740.    EnableCardInterrupt(&card1, InputSBISR);
  741.    EnableCardInterrupt(&card2, OutputSBISR);
  742.  
  743.  
  744.    // Fill playback buffers with silence and mark them as filled so the
  745.    // playback of the actual file will start delayed by 2+NUMBUFS buffers.
  746.    for (b=0; b<NUMBUFS; b++)
  747.       for (i=0; i<BUFSIZE/2; ++i) {
  748.          *((char *)outbuffers[b] + i) = 0x80;
  749.       }
  750.    NumFilledOutBufs = NUMBUFS;
  751.  
  752.    // fill both halves of output DMA buffer
  753.    FillOutputBuffer();
  754.    FillOutputBuffer();
  755.  
  756.  
  757.    // Start recording
  758.    SetupInputDMA(inpage,inoffset,count,TIMECONST);
  759.  
  760.    // Wait until several buffers have been written to disk before starting
  761.    // playback.  This gives the recording section a head start.  This is
  762.    // necessary because the playback section tries to fill all of its buffers,
  763.    // so there must be that much data already on the disk.
  764.    do {
  765.       WriteBufsToDisk(rech);
  766.    } while (NumBufsWritten < NUMBUFS);
  767.  
  768.  
  769.    // Now start playing.
  770.    SetupOutputDMA(outpage,outoffset,count,TIMECONST);
  771.  
  772.  
  773.    while (!done) {
  774.       WriteBufsToDisk(rech);     // write all full input buffers
  775.       ReadBufsFromDisk(playh);   // fill all empty output buffers
  776.  
  777.       gotoxy(1,3);
  778.       printf("Number of buffers written to disk: %5d\n",NumBufsWritten);
  779.  
  780.       if (kbhit()) {
  781.          c=getch();
  782.          switch (c) {
  783.             case ESC:
  784.                done=true;
  785.                break;
  786.             // Used to have other stuff here in a previous program.
  787.             default:
  788.                done=true;
  789.          }
  790.       }
  791.    }
  792.  
  793.    // Stop recording and playing.
  794.    dspout(&card1,dspcmdHaltDMA8);
  795.    dspout(&card2,dspcmdHaltDMA8);
  796.  
  797.    DisableCardInterrupt(&card1);
  798.    DisableCardInterrupt(&card2);
  799. }
  800.